Java进阶-- 利用有限制通配符来提升API的灵活性

词条

通配符— ‘?’

有限制的通配符 —

  • ? extends E
  • ? super E

PECS — producer-extends, consumer-super

结论

输入参数是生产者或消费者时使用’?’

所有的compare和comparator都是consumer

声明方法时,用’?’取代仅出现一次的的类型参数

优势体现——论其必要性

我们先看之前讲 优先考虑泛型时的一个示例:

考虑为它增加一个方法:

这样的定义,在使用中会存在不灵活的问题:因为类型在定义之后就不可变了,如果想要将一个非E类型的Iterable push到Stack中是不可能的,这样的事实我们显然不能接受,那么该如何修改呢?答案就在本篇的主角——通配符’?’上

将上述API的参数修改为:

1
Iterable<? extends E> src

即可达到目的

说完? extends,再来看? super,同样考虑Stack的API,这一次是将Stack中的元素全部弹出到目标集合

理想状态下,所有的子类型的Stack都可以弹出到父类型的集合,但上图的API并不能实现这一目的;我们需要这样修改:

将参数类型修改为:

1
Collection<? super E> dst

这样一来,任意继承自父类型的Stack就都可以pop到父类型集合了,是不是很带感

有的同学可能会产生疑惑了,一会儿extends,一会儿super,好晕

还好有规律可总结:

PECS——producer-extends, consumer-super

即参数列表中 生产者总是使用? extends,而消费者则总是使用 ? super(不了解生产者、消费者设计模式的同学请自行翻书)

高级应用

原始API声明:

1
<T extends Comparable<T>> T max1(List<T> list)

修改后的API声明:

1
<T extends Comparable<? super T>> T max2(List<? extends T> list)

这一修改后的API,pecs都用上了,真有必要弄这么复杂了?

答案是肯定的

1
List<ScheduleFuture<?>> futures = ...;

这一futures不能调用max1方法,原因在于ScheduleFuture扩展的是Compare接口的Delayed接口的子接口,它可以与任意Delayed进行compare;而max2方法这样的声明就不会有这问题

E 与 ‘?’

1
2
<E> void swap(List<E> list, int i, int j)
void swap(List<?> list, int i, int j)

你看上哪个了呢?

从灵活性的角度考虑,肯定是第二种好一些,但在类型参数不止一个时,就不能用’?’ 而要用类型参数了

使用第二种时需要注意:

只能把null放入List<?>中,这时为了保证灵活性,就需要写一个辅助捕捉类型的方法

1
2
3
4
5
6
7
void swap(List<?> list, int i, int j){
swapTrick(list, i, j);
}

<E> void swapTrick(List<E> list, int i, int j){
list.set(i, list.set(j, list.get(i)));
}

get

这一篇看起来似乎有点绕,但我们真正在项目中写公共库API时,其实是需要用到’?’的,不是为了装,是真的能提升API的适用范围,减少工作量

不过任何收获都是要付出代价的,在写出高适用度的API时,一定记得写单元测试,进行高覆盖度的验证,保证准确性

Powered by KyleCe

Copyright © 2015 - 2019 KyleCe All Rights Reserved.

访客数 : | 访问量 :